/*
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/gpu/graphite/PipelineData.h"

#include "src/core/SkChecksum.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"

namespace skgpu::graphite {
PipelineDataGatherer::PipelineDataGatherer(Layout layout) : fUniformManager(layout) {}

void PipelineDataGatherer::resetWithNewLayout(Layout layout)
{
    fUniformManager.resetWithNewLayout(layout);
    fTextureDataBlock.reset();
}

#ifdef SK_DEBUG
void PipelineDataGatherer::checkReset()
{
    SkASSERT(fTextureDataBlock.empty());
    SkASSERT(fUniformManager.isReset());
}

void PipelineDataGatherer::setExpectedUniforms(SkSpan<const Uniform> expectedUniforms)
{
    fUniformManager.setExpectedUniforms(expectedUniforms);
}
#endif // SK_DEBUG

// //////////////////////////////////////////////////////////////////////////////////////////////////
UniformDataBlock *UniformDataBlock::Make(const UniformDataBlock &other, SkArenaAlloc *arena)
{
    static constexpr size_t kUniformAlignment = alignof(void *);
    char *mem = static_cast<char *>(arena->makeBytesAlignedTo(other.size(), kUniformAlignment));
    memcpy(mem, other.data(), other.size());

    return arena->make([&](void *ptr) { return new (ptr)UniformDataBlock(SkSpan<const char>(mem, other.size())); });
}

uint32_t UniformDataBlock::hash() const
{
    return SkChecksum::Hash32(fData.data(), fData.size());
}

// //////////////////////////////////////////////////////////////////////////////////////////////////
TextureDataBlock *TextureDataBlock::Make(const TextureDataBlock &other, SkArenaAlloc *arena)
{
    return arena->make([&](void *ptr) { return new (ptr)TextureDataBlock(other); });
}

bool TextureDataBlock::operator == (const TextureDataBlock &other) const
{
    if (fTextureData.size() != other.fTextureData.size()) {
        return false;
    }

    for (size_t i = 0; i < fTextureData.size(); ++i) {
        if (fTextureData[i] != other.fTextureData[i]) {
            return false;
        }
    }

    return true;
}

uint32_t TextureDataBlock::hash() const
{
    uint32_t hash = 0;

    for (auto &d : fTextureData) {
        SamplerDesc samplerKey = std::get<1>(d);
        hash = SkChecksum::Hash32(&samplerKey, sizeof(samplerKey), hash);

        // Because the lifetime of the TextureDataCache is for just one Recording and the
        // TextureDataBlocks hold refs on their proxies, we can just use the proxy's pointer
        // for the hash here.
        uintptr_t proxy = reinterpret_cast<uintptr_t>(std::get<0>(d).get());
        hash = SkChecksum::Hash32(&proxy, sizeof(proxy), hash);
    }

    return hash;
}

#ifdef SK_DEBUG
UniformExpectationsValidator::UniformExpectationsValidator(PipelineDataGatherer *gatherer,
    SkSpan<const Uniform> expectedUniforms)
    : fGatherer(gatherer)
{
    fGatherer->setExpectedUniforms(expectedUniforms);
}
#endif
} // namespace skgpu::graphite
